package com.oromwin.repos.client;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;

import com.oromwin.repos.common.Common;
import com.oromwin.repos.common.Logger;
import com.oromwin.repos.common.ReposClientConfig;
//import com.oromwin.repos.common.ReposClientConfig;
import com.oromwin.repos.common.ReposException;

public abstract class AbstractSocketClient implements SocketClient {
    private Log log = Logger.getLog(AbstractSocketClient.class);
    private Map _socket = new HashMap();
    private Object _lock = new Object();
    
    protected synchronized String connect(String hostname, int port) throws ReposException {
        return connect(hostname, port, ReposClientConfig.getDefaultTimeout());
    }

    protected String connect(String hostname, int port, int timeout) throws ReposException {
        InetAddress addr = null;
        SocketAddress sockaddr = null;
        String id = null;
        synchronized (_lock) {
            try { // generated unique id for every socket client
                Thread.sleep(100);
                id = Common.datetime("yyyy-MM-dd'T'HH:mm:ss,SSS'Z'");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            addr = InetAddress.getByName(hostname);
            sockaddr = new InetSocketAddress(addr, port);
            Socket socket = new Socket();
            _socket.put(id, socket);
            socket.connect(sockaddr, timeout);
        } catch (UnknownHostException e) {
            throw ReposException.create(log, e, "Can't connect to server, hostname {" + hostname + "}");
        } catch (SocketTimeoutException e) {
            throw ReposException.create(log, e, "Can't connect to server, timeout {" + timeout + "}");
        } catch (IOException e) {
            throw ReposException.create(log, e, "Can't connect to server, socket {" + _socket.toString() + "}");
        }
        return id;
    }

    protected synchronized void disconnect(String id) throws ReposException {
        try {
            if (_socket.containsKey(id)) {
                Socket socket = (Socket) _socket.get(id);
                socket.close();
                _socket.remove(id);
            }
        } catch (IOException e) {
            throw ReposException.create(log, e, "Failed to disconnect socket connection, socket {" + _socket.toString() + "}");
        }
    }

    protected DataOutputStream getDataOutputStream(String id) throws ReposException {
        DataOutputStream dos = null;
        try {
            if (_socket.containsKey(id)) {
                Socket socket = (Socket) _socket.get(id);
                dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
            } else {
                throw ReposException.create(log, "Failed to get socket from socketMap{" + id + "}");
            }
        } catch (IOException e) {
            throw ReposException.create(log, e, "Failed to get output stream, socket {" + _socket.toString() + "}");
        }
        return dos;
    }

    protected DataInputStream getDataInputStream(String id) throws ReposException {
        DataInputStream dis = null;
        try {
            if (_socket.containsKey(id)) {
                Socket socket = (Socket) _socket.get(id);
                dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            }else {
                throw ReposException.create(log, "Failed to get socket from socketMap{" + id + "}");
            }
        } catch (IOException e) {
            throw ReposException.create(log, e, "Failed to get input stream, socket {" + _socket.toString() + "}");
        }
        return dis;
    }
}
